﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using RemoteTestRunner.BusinessLogic.Models;
using RemoteTestRunner.BusinessLogic.Views;
using RemoteTestRunner.Contracts;

namespace RemoteTestRunner.BusinessLogic.Presenters
{
    public class TestRunnerPresenter : PresenterBase
    {
        private readonly ISynchronizeInvoke _syncView;
        private readonly ITestRunnerView _view;

        //RemoteTestRunner.WCFService.IRTRService RTRServiceResolver;
        private DataSet _testData;

        public TestRunnerPresenter(ITestRunnerView view)
        {
            _view = view;
            _syncView = view as ISynchronizeInvoke;

            _view.SubscribeTestResultSelected(DisplayTestResults);
            _view.SubscribeClosed(Closed);
            _view.SubscribeProjectSelected(ProjectSelected);
            //_view.SubscribeRtrServiceSettingsChanged(LoadProjectsInView);
            //_view.SubscribeReloadProjects(LoadProjectsInView);
            _view.SubscribeTestSelected(TestSelected);
            _view.SubscribeTestsFiltered(TestsFiltered);
            _view.SubscribeLoadPreviousTestRuns(LoadTestRunHistory);
            _view.SubscribeDeleteTestResults(DeleteTestRunHistoryItems);
            _view.SubscribeRunTest(RunTest);
            _view.SubscribeDataSourceSelected(DataSourceSelected);
            _view.SubscribeEmailTestResults(EmailTestResults);
            _view.SubscribeLoadTestFileRequested(DisplayTestResultsFile);

            //RtrSettings.SettingsUpdated += SettingsUpdated;
            RtrServiceResolver.DataChanged += RtrServiceResolver_DataChanged;
        }

        public override IViewBase View
        {
            get { return _view; }
        }

        public override void Start()
        {
            //RefreshModel();

            LoadProjectsInView(this, EventArgs.Empty);

            //DataView projects = new DataView ( _model.GetTestProjects());
            //_view.SetProjects(projects);

            //if (projects.Count == 1)
            //{
            //    _view.Project = projects[0];

            //    _view.SetTests();
            //}

            //_view.SetTests();

            _view.Show();
        }

        private void RunTest(object sender, EventArgs e)
        {
            try
            {
                var userState = new List<object>();

                var longRunning = IoC.Container.Resolve<LongRunningPresenter>();
                userState.Add(longRunning);

                RtrLib.RunTestDelegate runTestDelegate = RtrServiceResolver.Service.RunTest;
                userState.Add(runTestDelegate);

                runTestDelegate.BeginInvoke((int)_view.Test["ID"], _view.GetTestsToRun(),
                                            RtrSettings.TestTimeoutSeconds, RunTestCompleted, userState);

                //longRunning.
                longRunning.Description = "Running test(s)...";
                longRunning.Start();

                //_runStatusForm = new LongRunning();
                //_runStatusForm.ProcessDescription = "Running test(s)...";
                //_runStatusForm.ShowDialog(this);
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    ex.ToString(), "Test Run Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// Completion method for async RunTest.
        /// </summary>
        /// <param name="ar">The async result.</param>
        private void RunTestCompleted(IAsyncResult ar)
        {
            if (_syncView.InvokeRequired)
            {
                AsyncCallback callback = RunTestCompleted;
                _syncView.Invoke(callback, new object[] { ar });
                return;
            }

            var userState = (List<object>)ar.AsyncState;
            var longRunning = (LongRunningPresenter)userState[0];

            longRunning.Stop();

            var runTestDelegate = userState[1] as RtrLib.RunTestDelegate;

            TestResultSummary summary;

            try
            {
                summary = runTestDelegate.EndInvoke(ar);
            }
            catch (Exception ex)
            {
                summary = null;
                MessageBox.Show(
                    ex.ToString(), "Test Run Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (summary != null)
            {
                // Save the run that just completed for selecting.
                var savedId = summary.TestRunId;

                // re-populate the test runs now that we have a new one.
                // TODO come up with a better way to populate test runs.  Maybe use a file watcher (this might not work for the remote version).
                LoadTestRunHistory(this, EventArgs.Empty);

                var resultList = new List<TestResultSummary>();
                resultList.Add(summary);
                _view.SelectedTestResults = resultList;
            }
        }

        /// <summary>
        /// Displays the test results file.
        /// </summary>
        /// <param name="testResultsPath">The path to the test results file to display.</param>
        private void DisplayTestResults(string testResultsPath)
        {
            var result = RtrServiceResolver.Service.GetTestResult(testResultsPath);

            if (result == null)
            {
                // TODO remove requirement for forms.
                _view.DisplayInvalidResultsFileMessage();
            }
            else
            {
                _view.DisplayTestResults(result);
            }
        }

        private void DisplayTestResultsFile(object sender, EventArgs e)
        {
            if (_view.TestFile != null)
            {
                var result = RtrServiceResolver.Service.GetTestResult(_view.TestFile);
                _view.DisplayTestResults(result);
            }
        }

        private void DisplayTestResults(object sender, EventArgs e)
        {
            if (_view.TestResultSummary == null)
            {
                _view.DisplayTestResults(null);
            }
            else
            {
                var result = RtrServiceResolver.Service.GetTestResult(_view.TestResultSummary.Path);
                _view.DisplayTestResults(result);
            }
        }

        private void SettingsUpdated(object sender, EventArgs e)
        {
            LoadProjectsInView(this, EventArgs.Empty);
        }

        private void RtrServiceResolver_DataChanged(object sender, EventArgs e)
        {
            LoadProjectsInView(this, EventArgs.Empty);
        }

        private void LoadProjectsInView(object sender, EventArgs e)
        {
            _testData = RtrServiceResolver.Service.GetTestData();
            var projects = new DataView(_testData.Tables["Project"]);
            _view.SetProjects(projects);

            if (projects.Count == 1)
            {
                _view.Project = projects[0];
                ProjectSelected(projects[0]["ID"].ToString());
            }
            else
            {
                _view.Project = null;
                ProjectSelected(null);
            }
        }

        private void ProjectSelected(object sender, EventArgs e)
        {
            ProjectSelected(_view.Project["ID"].ToString());
        }

        private void ProjectSelected(string projectId)
        {
            DataView tests;
            if (projectId != null)
            {
                tests =
                    new DataView(
                        _testData.Tables["Test"],
                        "ProjectId = " + projectId,
                        string.Empty,
                        DataViewRowState.CurrentRows);
            }
            else
            {
                tests = new DataView();
            }

            _view.SetTests(tests);

            if (tests.Count == 1)
            {
                _view.Test = tests[0];
            }
            else
            {
                _view.Test = null;
            }
            TestSelected(this, EventArgs.Empty);
        }

        private void Closed(object sender, EventArgs e)
        {
            // Save user settings
            RtrSettings.Save();

            Application.Exit();
        }

        private void TestsFiltered(object sender, GenericEventArgs<bool> e)
        {
            RtrSettings.FilterTests = e.Value;

            DisplayTestInformation(sender, e);
        }

        private void TestSelected(object sender, EventArgs e)
        {
            DisplayTestInformation(sender, e);
        }

        private void DisplayTestInformation(object sender, EventArgs e)
        {
            if (_view.Test == null)
            {
                _view.SetTestInfo(new TestInfo());
            }
            else
            {
                _view.SetTestInfo(RtrServiceResolver.Service.GetTestRow((int)_view.Test["ID"]));
            }

            LoadTestClassesAndMethods();

            LoadTestRunHistory(sender, e);
            DisplayTestResults(sender, e);

            LoadDataSourceMenu();
        }

        private void LoadDataSourceMenu()
        {
            if (_view.Test == null)
            {
                _view.SetDataSources(null);
            }
            else
            {
                var testInfo = RtrServiceResolver.Service.GetTestRow((int)_view.Test["ID"]);
                var dsInfo = RtrServiceResolver.Service.GetDataSources(testInfo.ConfigPath);

                _view.SetDataSources(dsInfo);

                //if (dsInfo != null)
                //{
                //    foreach (DataSourceInfo item in dsInfo)
                //    {
                //        string targetDataSourcePath;
                //        if (!string.IsNullOrEmpty(item.DataSourceBaseUri))
                //        {
                //            targetDataSourcePath = item.DataSourceBaseUri;
                //        }
                //        else
                //        {
                //            targetDataSourcePath = item.DataSourcePath;
                //        }

                //        // Populate Test->View Data Source menu
                //        //menuItem = new ToolStripMenuItem(item.ConnectionStringName, null, new EventHandler(ViewDataSource_Click), item.Name);
                //        //menuItem.Tag = targetDataSourcePath;
                //        //mnuTestViewDataSource.DropDown.Items.Add(menuItem);
                //    }
                //}
            }
        }

        /// <summary>
        /// Fills the previous test runs.
        /// </summary>
        private void LoadTestRunHistory(object sender, EventArgs e)
        {
            if (_view.Test == null)
            {
                _view.SetTestResultHistory(null);
            }
            else
            {
                IList<TestResultSummary> testResults = RtrServiceResolver.Service.GetTestResultHistory((int)_view.Test["ID"]);

                // Sort the results by start time descending.
                var orderedResults = testResults.OrderBy(result => result.TestStartTime).Reverse();

                _view.SetTestResultHistory(orderedResults.ToList());
            }
        }

        private void LoadTestClassesAndMethods()
        {
            if (_view.Test == null)
            {
                _view.SetTestClassAndMethods(null);
            }
            else
            {
                IList<TestClass> testMethods = RtrServiceResolver.Service.GetTestMethods((int)_view.Test["ID"]);

                // Filter out non-data-driven tests if the option is turned on
                if (RtrSettings.FilterTests)
                {
                    for (var classIndex = testMethods.Count - 1; classIndex >= 0; classIndex--)
                    {
                        for (var methodIndex = testMethods[classIndex].TestMethods.Count - 1;
                             methodIndex >= 0;
                             methodIndex--)
                        {
                            if (!testMethods[classIndex].TestMethods[methodIndex].DataDriven)
                            {
                                testMethods[classIndex].TestMethods.RemoveAt(methodIndex);
                            }
                        }

                        // Remove any class nodes that no longer have any method nodes
                        if (testMethods[classIndex].TestMethods.Count == 0)
                        {
                            testMethods.RemoveAt(classIndex);
                        }
                    }
                }

                _view.SetTestClassAndMethods(testMethods);
            }
        }

        /// <summary>
        /// Deletes the test run history items.
        /// </summary>
        private void DeleteTestRunHistoryItems(object sender, EventArgs e)
        {
            if (_view.CanDeleteSelectedResults())
            {
                RtrServiceResolver.Service.DeleteTestResultHistoryItems(_view.SelectedTestResults);
                LoadTestRunHistory(sender, e);
            }
        }

        private void DataSourceSelected(object sender, GenericEventArgs<string> selectedSource)
        {
            var testInfo = RtrServiceResolver.Service.GetTestRow((int)_view.Test["ID"]);
            var dsInfo = RtrServiceResolver.Service.GetDataSources(testInfo.ConfigPath);

            var source = dsInfo.First(item => item.Name == selectedSource.Value);

            if (source != null)
            {
                string targetDataSourcePath;
                if (!string.IsNullOrEmpty(source.DataSourceBaseUri))
                {
                    targetDataSourcePath = source.DataSourceBaseUri;
                }
                else
                {
                    targetDataSourcePath = source.DataSourcePath;
                }

                try
                {
                    // Launch the data source file in the default application for the file type
                    Process.Start(targetDataSourcePath);
                }
                catch (Exception ex)
                {
                    // The file was not found
                    MessageBox.Show(
                        "The data source could not be opened.  Please ensure the file exists. \nFile: " +
                        targetDataSourcePath + "\n\n" + ex.Message,
                        "Error Opening File",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                }
            }
        }

        private void EmailTestResults(object sender, EventArgs e)
        {
            if (_view.TestResultSummary != null)
            {
                var result = RtrServiceResolver.Service.GetTestResult(_view.TestResultSummary.Path);
                RtrLib.StartEmail(result);
            }
        }

        private void CopyTestResultsToClipboard(object sender, EventArgs e)
        {
            //if (_view.TestResultSummary != null)
            //{
            //    TestResult result = RtrServiceResolver.Service.GetTestResult(_view.TestResultSummary.Path);

            //    string text = string.Empty;
            //    int row;

            //    foreach (ListViewItem lvItem in lvResults.SelectedItems)
            //    {
            //        foreach (TestResultInfo item in result.TestResults)
            //        {
            //            if (item.ExecutionId == lvItem.Tag.ToString())
            //            {
            //                text += item.ToString() + "\r\n\r\n========================\r\n\r\n";

            //                break;
            //            }
            //            else
            //            { // Search in any data-driven results
            //                row = 0;

            //                foreach (TestResultInfo ddItem in item.DataDrivenResults)
            //                {
            //                    row++;

            //                    if (ddItem.ExecutionId == lvItem.Tag.ToString())
            //                    {
            //                        text += string.Format("[row: {0}]", row) +
            //                            ddItem.ToString() + "\r\n\r\n========================\r\n\r\n";

            //                        break;
            //                    }
            //                }
            //            }
            //        }
            //    }

            //    Clipboard.SetText(text);
            //}
        }
    }
}